﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Xml.Linq;
using Comdiv.QWeb.Serialization.BxlParser;

namespace Comdiv.QWeb.Bxl {
	public class Parser {
		private readonly string _code;
		private readonly string _filename;
		private readonly ParserOptions _options;

		public Parser(string code = "", string filename = "_text_", ParserOptions options = ParserOptions.None) {
			_code = code;
			_filename = filename;
			_options = options;
		}

		public BxlToken[] Parse(string code, ParserOptions options) {
			return Parse(code, _filename, options);
		}

		public BxlToken[] Parse() {
			return Parse(_options);
		}

		public BxlToken[] Parse(ParserOptions options) {
			return Parse(_code, _filename, options);
		}

		public BxlToken[] Parse(string code, string filename, ParserOptions options) {
			var tokenizer = new BxlTokenizer();
			if (options.HasFlag(ParserOptions.NoElements)) tokenizer = tokenizer.NoElements;
			return tokenizer.Tokenize(code, filename);
		}

		public BxlTree ParseTree() {
			return ParseTree(_options);
		}

		public BxlTree ParseTree(ParserOptions options) {
			return ParseTree(_code, _filename, options);
		}

		public BxlTree ParseTree(string code) {
			return ParseTree(code, _filename, _options);
		}

		public BxlTree ParseTree(string code, string filename, ParserOptions options) {
			var tokens = Parse(code, filename, options);
			return new BxlTree(tokens);
		}

		public BxlToken[][] ParseByLine(string code, ParserOptions options) {
			return ParseByLine(code, _filename, options);
		}

		public BxlToken[][] ParseByLine() {
			return ParseByLine(_options);
		}

		public BxlToken[][] ParseByLine(ParserOptions options) {
			return ParseByLine(_code, _filename, options);
		}

		public BxlToken[][] ParseByLine(string code, string filename, ParserOptions options) {
			var result = new List<BxlToken[]>();
			var reader = new StringReader(code);
			string line;
			var state = 1;
			var tokenizer = new BxlTokenizer();
			var linenumber = 0;
			while (null != (line = reader.ReadLine())) {
				tokenizer.SetInitialState(state);
				try {
					result[linenumber] = tokenizer.Tokenize(line, filename);
					state = tokenizer.GetFinishState();
				}
				catch (Exception ex) {
					result[linenumber] = new[] {new BxlToken(BxlTokenType.Error) {Value = ex.ToString()},};
					state = 1;
				}
				linenumber++;
			}
			return result.ToArray();
		}

		public XElement ParseXml(string code, ParserOptions options) {
			return ParseXml(code, _filename, options);
		}

		public XElement ParseXml() {
			return ParseXml(_options);
		}

		public XElement ParseXml(ParserOptions options) {
			return ParseXml(_code, _filename, options);
		}

		public XElement ParseXml(string code, string filename, ParserOptions options) {
			var tokens = Parse(code, filename, options);
			var generator = new BxlXmlGenerator();
			var result = generator.Generate(tokens, filename,
			                                !(options.HasFlag(ParserOptions.NoLexData)),
			                                options.HasFlag(ParserOptions.SafeAttributeNames),
			                                options.HasFlag(ParserOptions.OnlyCodeAttribute)
				);
			return result;
		}
	}
}